package org.deltaset.meta.internal;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.deltaset.meta.MetaField;

/**
 * represents the parameter array of a MetaAction this is either a list of all
 * of the parameters or a parameter java bean class;
 *
 * @author dmakariev
 */
class MetaParameter implements Serializable {

    //the metafields and parameterTypes should be with the same size and are without the paging parameters.
    MetaParameter(List<MetaField> metaFields, Class parameterClass, Class[] parameterTypes) {
        this.metaFields = metaFields;
        this.parameterClass = parameterClass;
        this.parameterTypes = parameterTypes;
//        if (metaFields.size() != parameterTypes.length) {
//            throw new IllegalArgumentException("ERROR the metafields and parameterTypes should be with the same size");
//        }
    }
    private final List<MetaField> metaFields;
    private final Class parameterClass;
    private final Class[] parameterTypes;

    Object newInstance() {
        if (null != parameterClass) {
            return ReflectionUtils.newInstance(parameterClass);
        }
        return mappedObject();
    }

    Object[] prepareParameter(final Object parameterValue) {
        if (parameterValue instanceof Map) {
            final Map<String, Object> parameterMap = (Map) parameterValue;
            final int size = parameterMap.size();
            final Object[] parameters = new Object[size];
            for (int i = 0; i < size; i++) {
                final String fieldName = MetaFieldImpl.fieldName(i);
                final Class originalType = this.parameterTypes[i];
                final Object value = parameterMap.get(fieldName);
                parameters[i] = convert(originalType, value);
            }
            return parameters;
        } else {
            return new Object[]{parameterValue};
        }
    }

    private Object convert(final Class originalType, final Object value) {

        //FIXME: wierd case.. needs extensive testing.. this is just a workaround 
        if (value instanceof BigDecimal) {
            final BigDecimal bigDecimal = (BigDecimal) value;
            if (originalType.equals(Integer.class)
                    || originalType.equals(int.class)) {
                return bigDecimal.intValue();
            } else if (originalType.equals(Long.class)
                    || originalType.equals(long.class)) {
                return bigDecimal.longValue();
            } else if (originalType.equals(Double.class)
                    || originalType.equals(double.class)) {
                return bigDecimal.doubleValue();
            } else if (originalType.equals(Short.class)
                    || originalType.equals(short.class)) {
                return bigDecimal.shortValue();
            } else if (originalType.equals(Float.class)
                    || originalType.equals(float.class)) {
                return bigDecimal.floatValue();
            } else if (originalType.equals(Byte.class)
                    || originalType.equals(byte.class)) {
                return bigDecimal.byteValue();
            }
        } else {
            if (originalType.equals(Integer.class)
                    || originalType.equals(int.class)) {
                return ((Integer) value).intValue();
            } else if (originalType.equals(Long.class)
                    || originalType.equals(long.class)) {
                return ((Long) value).longValue();
            } else if (originalType.equals(Double.class)
                    || originalType.equals(double.class)) {
                return ((Double) value).doubleValue();
            } else if (originalType.equals(Short.class)
                    || originalType.equals(short.class)) {
                return ((Short) value).shortValue();
            } else if (originalType.equals(Float.class)
                    || originalType.equals(float.class)) {
                return ((Float) value).floatValue();
            } else if (originalType.equals(Byte.class)
                    || originalType.equals(byte.class)) {
                return ((Byte) value).byteValue();
            }
        }

        return originalType.cast(value);
    }

    private Map<String, Object> mappedObject() {
        return new HashMap<String, Object>();
    }

    public List<MetaField> getMetaFields() {
        return metaFields;
    }
}
